﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Data;
using iTFF.WPF.CP;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
using iTFF.WPF.UI;
using iTFF.WPF.Model.Toaster;

namespace iTFF.WPF.ViewModel
{
    class ToasterViewModel : WorkspaceViewModel
    {
        #region Constants

        /// <summary>
        /// The amount of milliseconds to display the fade animation
        /// </summary>
        static readonly int FADE_ANIMATION_MS = 1000;

        #endregion

        #region Fields

        ToasterTrackViewModel _toasterTrack;
        UserControl _skinUC;
        double _viewActualWidth;
        double _viewActualHeight;
        Storyboard _storyboard;
        bool _storyboardCreated;

        RelayCommand<RoutedEventArgs> _sizeChangedCommand;
        RelayCommand _mouseEnterCommand;
        RelayCommand _mouseLeaveCommand;
        RelayCommand<MouseButtonEventArgs> _mouseDownCommand;

        #endregion

        #region Constructor

        public ToasterViewModel(ToasterTrackViewModel toasterTrack)
        {
            ToasterTrack = toasterTrack;
            SkinUC = CreateUCForSelectedToasterSkin();
        }

        #endregion

        #region Properties

        public ToasterTrackViewModel ToasterTrack
        {
            get { return _toasterTrack; }
            set
            {
                if (_toasterTrack != value)
                {
                    _toasterTrack = value;

                    // attach the skin to the track
                    if (CurrentSkin != null)
                        _toasterTrack.ToasterSkin = new ToasterSkinViewModel(CurrentSkin);
                    else
                        _toasterTrack.ToasterSkin = null;

                    OnPropertyChanged("ToasterTrack");
                }
            }
        }

        public UserControl SkinUC
        {
            get { return _skinUC; }
            set
            {
                if (_skinUC != value)
                {
                    _skinUC = value;
                    OnPropertyChanged("SkinUC");
                }
            }
        }

        public double LeftPosition
        {
            get { return SystemParameters.WorkArea.Width - _viewActualWidth; }
        }

        public double TopPosition
        {
            get { return SystemParameters.WorkArea.Height - _viewActualHeight; }
        }

        #endregion

        #region Settings properties

        public int ShowDuration
        {
            get { return Settings.Default.ToasterShowTimer; }
        }

        public bool HideOnClick
        {
            get { return Settings.Default.ToasterHideOnClick; }
        }

        public bool ReappearOnMouseOver
        {
            get { return Settings.Default.ToasterReappearOnMouseOver; }
        }

        #endregion

        #region StoryBoard

        /// <summary>
        /// Creates the storyboard
        /// </summary>
        /// <param name="view"></param>
        public void CreateStoryBoard(Window view)
        {
            if (!_storyboardCreated)
            {
                _storyboard = new Storyboard();
                _storyboard.Children.Add(new DoubleAnimation(0.0f, 1.0f, new Duration(TimeSpan.FromMilliseconds(FADE_ANIMATION_MS))));

                // create a Parallel time line to begin this animation after the previous one(s)
                var par = new ParallelTimeline(TimeSpan.FromMilliseconds(FADE_ANIMATION_MS));
                par.Children.Add(
                    new DoubleAnimation(1.0f, new Duration(TimeSpan.FromSeconds(this.ShowDuration)))
                    );
                _storyboard.Children.Add(par);

                // create a Parallel time line to begin this animation after the previous one(s)
                par = new ParallelTimeline(TimeSpan.FromMilliseconds(FADE_ANIMATION_MS + this.ShowDuration * 1000));
                par.Children.Add(
                    new DoubleAnimation(1.0f, 0.0f, new Duration(TimeSpan.FromMilliseconds(FADE_ANIMATION_MS)))
                    );
                _storyboard.Children.Add(par);

                Storyboard.SetTarget(_storyboard, view);
                Storyboard.SetTargetProperty(_storyboard, new PropertyPath(Window.OpacityProperty));

                _storyboard.Completed += OnStoryboardCompleted;
                _storyboard.Freeze();
                view.Loaded += OnViewLoaded;

                _storyboardCreated = true;
            }
        }

        /// <summary>
        /// Shows the toaster at the correct animation state (seeking it)
        /// </summary>
        public void ShowToast()
        {
            _storyboard.Seek(TimeSpan.FromMilliseconds(FADE_ANIMATION_MS));
        }

        /// <summary>
        /// Starts the StoryBoard
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnViewLoaded(object sender, RoutedEventArgs e)
        {
            _storyboard.Begin();
        }

        /// <summary>
        /// Closes the window when the storyboard is completed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnStoryboardCompleted(object sender, EventArgs e)
        {
            if (this.CloseCommand.CanExecute(null))
                this.CloseCommand.Execute(null);
        }

        #endregion

        #region Commands

        /// <summary>
        /// Update the private ActualWidth and Height and then reacalc Top and Left of the view
        /// </summary>
        public ICommand SizeChangedCommand
        {
            get
            {
                if (_sizeChangedCommand == null)
                    _sizeChangedCommand = new RelayCommand<RoutedEventArgs>(param => OnSizeChangedCommand(param));
                return _sizeChangedCommand;
            }
        }

        /// <summary>
        /// Update the private ActualWidth and Height and then reacalc Top and Left of the view
        /// </summary>
        /// <param name="param"></param>
        private void OnSizeChangedCommand(RoutedEventArgs param)
        {
            // update actual width & height
            var view = param.Source as Window;
            if (view != null)
            {
                _viewActualHeight = view.ActualHeight;
                _viewActualWidth = view.ActualWidth;
                OnPropertyChanged("LeftPosition");
                OnPropertyChanged("TopPosition");
            }
        }

        /// <summary>
        /// Makes the toaster stay if Reappear on mouse over is True in the settings
        /// </summary>
        public ICommand MouseEnterCommand
        {
            get
            {
                if (_mouseEnterCommand == null)
                    _mouseEnterCommand = new RelayCommand(param => OnMouseEnterCommand(param));
                return _mouseEnterCommand;
            }
        }

        /// <summary>
        /// Makes the toaster stay if Reappear on mouse over is True in the settings
        /// </summary>
        /// <param name="param"></param>
        private void OnMouseEnterCommand(object param)
        {
            if (ReappearOnMouseOver)
            {
                _storyboard.Pause();
                _storyboard.Seek(TimeSpan.FromMilliseconds(FADE_ANIMATION_MS));
            }
        }

        /// <summary>
        /// Resumes the animation
        /// </summary>
        public ICommand MouseLeaveCommand
        {
            get
            {
                if (_mouseLeaveCommand == null)
                    _mouseLeaveCommand = new RelayCommand(param => OnMouseLeaveCommand(param));
                return _mouseLeaveCommand;
            }
        }

        /// <summary>
        /// Resumes the animation
        /// </summary>
        /// <param name="param"></param>
        private void OnMouseLeaveCommand(object param)
        {
            _storyboard.Resume();
        }

        /// <summary>
        /// Closes the window if HideOnClick is true
        /// </summary>
        public ICommand MouseDownCommand
        {
            get
            {
                if (_mouseDownCommand == null)
                    _mouseDownCommand = new RelayCommand<MouseButtonEventArgs>(param => OnMouseDownCommand(param));
                return _mouseDownCommand;
            }
        }

        /// <summary>
        /// Closes the window if HideOnClick is true
        /// </summary>
        /// <param name="param"></param>
        private void OnMouseDownCommand(MouseButtonEventArgs param)
        {
            if (HideOnClick && CloseCommand.CanExecute(null))
                CloseCommand.Execute(null);
        }
        #endregion

        #region Create UC

        /// <summary>
        /// Creates the uc for the selected toaster skin
        /// </summary>
        private UserControl CreateUCForSelectedToasterSkin()
        {
            var skin = ControlPanel.Instance.ToasterControlPanel.Skins.FirstOrDefault(s => s.Name == Settings.Default.ToasterSelectedSkin);

            // don't display any toaster if skin is invalid
            if (skin == null)
                return null;

            try
            {
                var uc = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(skin.FullName) as UserControl;

                if (uc != null)
                {
                    // create the binding for the datacontext
                    Binding b = new Binding()
                    {
                        Source = this,
                        Path = new System.Windows.PropertyPath("ToasterTrack")
                    };

                    uc.SetBinding(UserControl.DataContextProperty, b);
                }

                return uc;
            }
            catch (Exception)
            {
                // don't do nothing
                return null;
            }
        }

        #endregion

        #region Private helpers

        private ToasterSkin CurrentSkin
        {
            get { return ControlPanel.Instance.ToasterControlPanel.Skins.FirstOrDefault(s => s.Name == Settings.Default.ToasterSelectedSkin); }
        }

        #endregion

        #region Dispose

        protected override void OnDispose()
        {
            // remove t he binding
            if (SkinUC != null)
                BindingOperations.ClearBinding(SkinUC, UserControl.DataContextProperty);
            base.OnDispose();
        }

        #endregion

    }
}
